﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Data.SqlServerCe;
using System.Text;
using Log;

namespace RegLog.RegistroLog
{
    /// <summary>
    /// Classe para registro de log em base de dados do Microsoft SQL Compact Edition
    /// </summary>
    public class LogSQLCE : ILog
    {
        /// <summary>
        /// Definie a senha do banco de dados.
        /// </summary>
        public string Senha { get; private set; }
        /// <summary>
        /// Define o caminho onde está salvo o arquivo de banco de dados.
        /// </summary>
        public string Caminho { get; private set; }
        /// <summary>
        /// Define o nome do arquivo de banco de dados .sdf
        /// </summary>
        public string DB { get; private set; }
        /// <summary>
        /// Objeto de conexão com o banco de dados do SQL CE.
        /// </summary>
        public SqlCeConnection Conexao { get; private set; }

        /// <summary>
        /// Construtor que define os dados da base que será acessada. Caso o arquivo de banco de dados não seja encontrado, um novo será criado com os dados dos parâmetros informados.
        /// </summary>
        /// <param name="caminho">Caminho completo para acesso ao arquivo .sdf do banco de dados.</param>
        /// <param name="db">Nome do arquivo de banco de dados SQL CE.</param>
        /// <param name="senha">Senha que será utilizada para acessar o banco de dados.</param>
        public LogSQLCE(string caminho, string db, string senha)
        {
            if (!Directory.Exists(caminho))
                Directory.CreateDirectory(caminho);
            DB = db.LastIndexOf(".sdf") == db.Length -4 ? db : db + ".sdf";
            Caminho = System.IO.Path.Combine(caminho, DB);
            Senha = senha;
            try 
	        {
		        if (File.Exists(Caminho)) Conectar();
                else CriarBanco();

                if (!VerificarTabela()) CriarTabelaLog();
	        }
	        catch (Exception ex)
	        {
		        throw ex;
	        }
        }

        private bool VerificarTabela()
        {
            var sql = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'RegistroLog'";
            if (Conexao == null) Conectar();
            var cmd = new SqlCeCommand(sql, Conexao);
            cmd.CommandType = System.Data.CommandType.Text;
            SqlCeDataReader dr = cmd.ExecuteResultSet(ResultSetOptions.Scrollable);
            //var res = cmd.ExecuteReader();
            return dr.HasRows;
        }

        private void Conectar()
        {
            try
            {
                if (Conexao == null) Conexao = new SqlCeConnection();
                if (Conexao.State != System.Data.ConnectionState.Open)
                {
                    var strConexao = string.Format("Data Source={0};Encrypt Database=True;Password='{1}';File Mode=shared read;Persist Security Info=False;", Caminho, Senha);
                    //Conexao = new SqlCeConnection(strConexao);
                    Conexao.ConnectionString = strConexao;
                    Conexao.Open();
                }                
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void CriarBanco()
        {
            try
            {
                if (!File.Exists(Caminho))
                {
                    var sql = string.Format("Data Source={0}; PASSWORD='{1}'; CASE SENSITIVE=FALSE", Caminho, Senha); // ENCRYPTION ON COLLATE Latin1_General_CI_AS
                    var engine = new SqlCeEngine(sql);
                    engine.CreateDatabase();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void CriarTabelaLog()
        {
            try
            {
                Conectar();
                var sql = new StringBuilder();
                sql.AppendLine("CREATE TABLE RegistroLog (");
                sql.AppendLine("dataHora DATETIME DEFAULT GETDATE() NOT NULL, ");
                sql.AppendLine("tipoLog NVARCHAR(20)  NOT NULL, ");
                sql.AppendLine("descricao NVARCHAR(200)  NOT NULL, ");
                sql.AppendLine("excecaoMensagem NVARCHAR(500)  NULL, ");
                sql.AppendLine("excecaoSource NVARCHAR(500)  NULL, ");
                sql.AppendLine("excecaoStackTrace NVARCHAR(500)  NULL, ");
                sql.AppendLine("excecaoTargetSite NVARCHAR(500)  NULL)");
                var cmd = new SqlCeCommand(sql.ToString(), Conexao);
                cmd.ExecuteNonQuery();
                cmd.Dispose();
            }
            catch (Exception ex)
            {                
                throw ex;
            }
        }

        /// <summary>
        /// Registra o evento de log no banco de dados do SQL CE.
        /// </summary>
        /// <param name="sender">Objeto responsável pelo registro do log.</param>
        /// <param name="e">Objeto que representa o evento de log.</param>
        /// <param name="ret">Retorno que informa sucesso ou falha (-1) no registro de log.</param>
        public void Log(object sender, EventosLogEventArgs e, out int ret)
        {
            if (!string.IsNullOrWhiteSpace(Caminho))
            {
                Conectar();
                using (SqlCeTransaction tr = Conexao.BeginTransaction())
                {
                    try
                    {
                        var cmd = Conexao.CreateCommand();
                        cmd.Transaction = tr;

                        var sql = new StringBuilder();
                        sql.AppendLine("INSERT INTO RegistroLog (dataHora, tipoLog, descricao, ");
                        sql.AppendLine("excecaoMensagem, excecaoSource, excecaoStackTrace, excecaoTargetSite) ");
                        sql.AppendFormat("VALUES ('{0}','{1}', '{2}', '{3}', '{4}', '{5}', '{6}')", e.DataHora.ToString("yyyy-MM-dd HH:mm:ss"), e.TipoLogString, e.Descricao, e.Exception.Message,
                            e.Exception.Source, e.Exception.StackTrace, e.Exception.TargetSite);
                        cmd.CommandText = sql.ToString();
                        ret = cmd.ExecuteNonQuery();
                        tr.Commit();
                    }
                    catch (Exception ex)
                    {
                        ret = -1;
                        tr.Rollback();
                        throw ex;
                    }
                }
                Conexao.Close();
            }
            else
            {
                ret = -1;
                throw new Exception("A base de dados não foi encontrada.");
            }
        }
    }
}
